import axios from 'axios';
import { message } from 'antd';
import { t } from 'src/locales';

import type {
  AxiosRequestConfig,
  AxiosInstance,
  AxiosResponse,
  AxiosError,
  InternalAxiosRequestConfig,
} from 'axios';

export class HttpRequest {
  private axiosInstance: AxiosInstance | null = null;
  private readonly options: AxiosRequestConfig = {};

  private RequestInterceptorsMap = new Map();
  private ResponseInterceptorsMap = new Map();

  constructor(options?: AxiosRequestConfig) {
    this.options = options ?? {};
    this.createAxios(options);
  }

  setRequestInterceptors(
    onFulfilled: (
      config: InternalAxiosRequestConfig,
    ) => InternalAxiosRequestConfig | Promise<InternalAxiosRequestConfig>,
    onRejected?: (error: AxiosError) => any,
  ) {
    if (!this.axiosInstance) return;
    const interceptor = this.axiosInstance?.interceptors.request.use(onFulfilled, onRejected);
    this.RequestInterceptorsMap.set(onFulfilled, interceptor);
  }

  setResponseInterceptors(
    onFulfilled: (config: AxiosResponse) => any,
    onRejected?: (error: AxiosError) => any,
  ) {
    if (!this.axiosInstance) return;
    const interceptor = this.axiosInstance?.interceptors.response.use(onFulfilled, onRejected);
    this.ResponseInterceptorsMap.set(onFulfilled, interceptor);
  }
  removeRequestInterceptors(
    onFulfilled: (config: InternalAxiosRequestConfig) => InternalAxiosRequestConfig,
  ) {
    if (!this.axiosInstance) return;
    const interceptor = this.RequestInterceptorsMap.get(onFulfilled);
    this.axiosInstance.interceptors.request.eject(interceptor);
  }
  removeResponseInterceptor(onFulfilled: (config: AxiosResponse) => AxiosResponse) {
    if (!this.axiosInstance) return;
    const interceptor = this.ResponseInterceptorsMap.get(onFulfilled);
    this.axiosInstance.interceptors.request.eject(interceptor);
  }

  createAxios(config?: AxiosRequestConfig) {
    this.axiosInstance = axios.create(config);
  }

  setHeader(headers: Record<string, any>): void {
    if (!this.axiosInstance) return;
    Object.assign(this.axiosInstance.defaults.headers, headers);
  }

  get<T = any>(url: string, options?: AxiosRequestConfig): Promise<T> {
    return this.request<T>({ url, method: 'GET', ...options });
  }
  post<T = any>(url: string, options?: AxiosRequestConfig): Promise<T> {
    return this.request<T>({ url, method: 'POST', ...options });
  }
  put<T = any>(url: string, options?: AxiosRequestConfig): Promise<T> {
    return this.request<T>({ url, method: 'PUT', ...options });
  }
  delete<T = any>(url: string, options?: AxiosRequestConfig): Promise<T> {
    return this.request<T>({ url, method: 'DELETE', ...options });
  }

  request<T>(config: AxiosRequestConfig): Promise<T> {
    const options = { ...this.options, ...config };
    return new Promise((resolve, reject) => {
      options.url;
      this.axiosInstance
        ?.request(options)
        .then((res: AxiosResponse<T>) => {
          const result: T = res?.data;
          resolve(result);
        })
        .catch((e: Error | AxiosError) => {
          reject(e);
        });
    });
  }
  jsonP<T>(config: {
    url: string;
    params: Record<string, any> & { callback: string };
  }): Promise<T> | any {
    const { url, params } = config;
    const { callback } = params;
    if (!url) {
      message.error(t('system.request.jsonp_not_url'));
      return;
    }
    if (!callback) {
      message.error(t('system.request.jsonp_not_callback'));
      return;
    }
    return new Promise((resolve, reject) => {
      try {
        (window as any)[callback] = (result: T) => {
          resolve(result);
        };
        const JSONP = document.createElement('script');
        JSONP.src = `${url}?callback=${callback}`;
        JSONP.type = 'text/javascript';
        document.body.appendChild(JSONP);
        JSONP.onload = () => {
          JSONP.remove();
        };
        JSONP.onerror = (err) => {
          reject(err);
        };
      } catch (e) {
        reject(e);
      }
    });
  }
}
